﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using CARE.Web.db;
using GraphInformation;
using ComplexNetGeneratorLib;
using EpidemicSimulator;
using EpidemicSimulator.stopconditions;
using System.Security.Permissions;
using System.IO;
using System.Reflection;
using QuickGraph;
using QuickGraph.Serialization;
using EpidemicSimulator.states;
using EpidemicSimulator.strategies;
using System.Net;
using System.Globalization;
using EpidemicSimulator.reports;
using QuickGraphBuilder;
using GraphMLComplexType;
using System.Text;

namespace CARE.Web {
	/// <summary>
	/// Summary description for GraphProviderService1
	/// </summary>
	[WebService(Namespace = "http://nosoifigthers.studentlive.pl/")]
	[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
	[System.ComponentModel.ToolboxItem(false)]

	// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
	[System.Web.Script.Services.ScriptService]
	public class GraphProviderService : System.Web.Services.WebService {
		[WebMethod]
		public Graph CreateGraph(String graphML, String stateMachineGraphML, String token, int MaxSimStep) {
			Simulation s = null;
			SimulationSetup simsetup = new SimulationSetup(graphML, stateMachineGraphML, 1, 1);
			s = SimulationFactory.createSimulation(simsetup, HttpContext.Current.Server.MapPath("App_Data"));//HttpContext.Current.Server.MapPath("App_Data/terror.graphml.xml"), HttpContext.Current.Server.MapPath("App_Data/sis.graphml.xml"));
			s.addStopCondition(new MaxSimStepNumberStopCondition(MaxSimStep));
			Application[token.ToString()] = s;

			Application[token] = s;

			return s.getSimulationResultForSilverlight();
		}

        [WebMethod(EnableSession = true)]
        public int getSimulationMaxTime()
        {
            Simulation s = Context.Session["simulation"] as Simulation;
            foreach (var c in s.stopConditions)
            {
                if (c is MaxSimStepNumberStopCondition)
                    return ((c as MaxSimStepNumberStopCondition)).getMaxSimTimeStepNumberStopCondition();
            }
            return int.MaxValue;
        }

		[WebMethod]
		public Graph CreateDiseaseModelGraph(string modelML) {
			Graph resultG = new Graph();

			var g = new AdjacencyGraph<DiseaseModelNode, DiseaseModelEdge>();
			StringReader reader = new StringReader(modelML);
			try {
				g.DeserializeFromGraphML(
					reader,
					id => new DiseaseModelNode(),
					new IdentifiableEdgeFactory<DiseaseModelNode, DiseaseModelEdge>(SimulationFactory.CreateEdge<DiseaseModelNode, DiseaseModelEdge>)
				);
			}
			catch (Exception e) {
				throw e;
			}
			reader.Close();

            var map = new Dictionary<DiseaseModelNode, GraphElement>();
            resultG.Vertices = new List<GraphElement>();
            //var list = new List<GraphElement>();
            for (int i = 0; i < g.VertexCount; i++)
            {
                var v = g.Vertices.ElementAt(i);
                var elem = new GraphElement();
                elem.ID = Guid.NewGuid();
                elem.GraphID = resultG.ID;
                elem.Keys = new List<PairKeyValue>();
                elem.Keys.Add(new PairKeyValue() { Key = "nodeColor", Value = v.nodeColor, ID = Guid.NewGuid(), ElementID = elem.ID });
                elem.Keys.Add(new PairKeyValue() { Key = "nodeX", Value = v.nodeX, ID = Guid.NewGuid(), ElementID = elem.ID });
                elem.Keys.Add(new PairKeyValue() { Key = "nodeY", Value = v.nodeY, ID = Guid.NewGuid(), ElementID = elem.ID });
                elem.Keys.Add(new PairKeyValue() { Key = "stateName", Value = v.stateName, ID = Guid.NewGuid(), ElementID = elem.ID });
                resultG.Vertices.Add(elem);
                map.Add(v, elem);
            }

            var list1 = new List<GraphEdge>();
            resultG.Edges = new List<GraphEdge>();
            for (int i = 0; i < g.Edges.Count(); i++)
            {
                var e = g.Edges.ElementAt(i);
                resultG.Edges.Add(new GraphEdge() { GraphID = resultG.ID, ID = Guid.NewGuid(), Source = map[e.Source].ID, Target = map[e.Target].ID, Condition = e.condition });
            }

			return resultG;
		}

		[WebMethod(EnableSession=true)]
		public Graph StartSimulation(SimulationSetupSL simsetup, String token) {
			Graph resultG = new Graph();

			try {
				Simulation s = null;

				if (simsetup.networkGraph != null) {
					var graph = simsetup.networkGraph.GetGraphFormSilverlight();
				}

                SimulationSetup simulationSetup = new SimulationSetup(simsetup.networkGraphML, simsetup.diseaseModelGraphML, simsetup.maxContactsPerStep, simsetup.minContactsPerStep);
                simulationSetup.condition = simsetup.condition;
                simulationSetup.networkGraph = simsetup.networkGraph;
				simulationSetup.stateChangePlansSL = simsetup.stateChangePlansSL;
				simulationSetup.stopConditionsSL = simsetup.stopConditionsSL;
				s = SimulationFactory.createSimulation(simulationSetup, HttpContext.Current.Server.MapPath("App_Data"));

                Context.Session["simulation"] = s;
				//Application[token] = s;

			    resultG = s.getSimulationResultForSilverlight();
			}
			catch (Exception e) {
				//TODO: add handling
                Console.Write(e.StackTrace.ToString());
			}
			return resultG;
		}

        [WebMethod(EnableSession = true)]
        public void StartSimulationInBackGround(SimulationSetupSL simsetup, int numOfExperiments)
        {
            SimulationSetup simulationSetup = new SimulationSetup(simsetup.networkGraphML, simsetup.diseaseModelGraphML, simsetup.maxContactsPerStep, simsetup.minContactsPerStep);
            simulationSetup.networkGraph = simsetup.networkGraph;
            simulationSetup.stateChangePlansSL = simsetup.stateChangePlansSL;
            simulationSetup.stopConditionsSL = simsetup.stopConditionsSL;

            SimulationManager simManager = new SimulationManager(simulationSetup, numOfExperiments, HttpContext.Current.Server.MapPath("App_Data"));
            simManager.runSimulationAsync();
            Context.Session["AsyncSimManager"] = simManager;
        }

        [WebMethod(EnableSession = true)]
        public ExperimentState[] getExporimentState()
        {
            try
            {
                SimulationManager simManager = (SimulationManager)Context.Session["AsyncSimManager"];
                return simManager.getExperimentState();
            }
            catch (Exception e)
            { }
            
            return null;
        }

        [WebMethod(EnableSession = true)]
        public SimulationSimpleReport getAsyncSimulationReport()
        {
            try
            {
                SimulationManager simManager = (SimulationManager)Context.Session["AsyncSimManager"];
                return simManager.getSimulationReport();
            }
            catch (Exception e)
            { }

            return null;
        }

		[WebMethod(EnableSession=true)]
		public void AddStateChangePlan(StateChangePlanSL plan, String token) {
			Simulation s = null;

			if (Application[token] != null) {
				s = (Simulation)Application[token.ToString()];
			}

			StateChangePlan stateChangePlan = null;

			Type type = Type.GetType(plan.stateChangeStrategyName, true);
			object newInstance = Activator.CreateInstance(type);

			StateChangeStrategy strategy = (StateChangeStrategy)newInstance;
			if (plan.inPersents) {
				stateChangePlan = new PersentageStateChangePlan(plan.value, strategy, plan.applyTime);
			}
			else {
				stateChangePlan = new NumericalStateChangePlan(plan.value, strategy, plan.applyTime);
			}
			s.addStateChangePlan(stateChangePlan);
		}

		[WebMethod]
		public Graph CreateGraphWithDiseaseModelGraph(String graphML, DiseaseModelGraph stateMachineGraph, String token, int MaxSimStep) {
			Simulation s = null;

			//s = SimulationFactory.createSimulation(graphML, stateMachineGraphML, HttpContext.Current.Server.MapPath("App_Data"));//HttpContext.Current.Server.MapPath("App_Data/terror.graphml.xml"), HttpContext.Current.Server.MapPath("App_Data/sis.graphml.xml"));
			//s.addStopCondition(new MaxSimStepNumberStopCondition(MaxSimStep));
			//Application[token.ToString()] = s;

			//var g = s.getSimulationResultForSilverlight();

			Application[token] = s;

			Graph resultG = new Graph();
			//resultG.Vertices = new GraphElement[g.VertexCount];
			//for (int i = 0; i < g.VertexCount; i++)
			//{
			//    var v = g.Vertices.ElementAt(i);
			//    resultG.Vertices[i] = v;
			//}


			//resultG.Edges = new GraphEdge[g.Edges.Count()];
			//for (int i = 0; i < g.Edges.Count(); i++)
			//{
			//    var e = g.Edges.ElementAt(i);
			//    resultG.Edges[i] = new GraphEdge(e.Source, e.Target);
			//}

			return resultG;
		}

        [WebMethod(EnableSession = true)]
		public Graph NextStep(String token) {
            Simulation s = Context.Session["simulation"] as Simulation;

            if (s == null) return null;

			s.nextSimStep();
			return s.getSimulationResultForSilverlight();
		}

		[WebMethod]
		public String[] GetAvailableMethodsOfCentrality() {
			MethodInfo[] methods = typeof(ComplexNetCentralityLib.ComplexNetCentrality).GetMethods(BindingFlags.Static | BindingFlags.Public);
			List<String> toReturn = new List<string>();

			foreach (var item in methods) {
				ParameterInfo[] infos = item.GetParameters();
				if (infos.Count() == 1) {
					if (infos[0].Name == "socialNetwork")
						toReturn.Add(item.Name);
				}
			}

			return toReturn.ToArray();
		}

		[WebMethod]
		public Graph ComputeCentrality(String MethodName, Graph input) {
            UndirectedGraph<SocialNetworkNode, EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode>> graph = input.GetGraphFormSilverlight();
            AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>> network = new AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>>();
            foreach (EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode> edge in graph.Edges)
            {
                EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType> e = new EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>(edge.Source, edge.Target);
                network.AddVerticesAndEdge(e);
            }

			Type complex = typeof(ComplexNetCentralityLib.ComplexNetCentrality);
			Graph centralityGraph = null;
			try {
				centralityGraph = (Graph)complex.GetMethod(MethodName).Invoke(null, new object[] { network });
				input.Merge(network);
			}
			catch (Exception e) {
				;
			}

			return input;
		}

        [WebMethod]
        public Graph ComputeCentralityFromGraphML(String MethodName, String graphML)
        {
            Graph resultG = new Graph();

            UndirectedGraph<SocialNetworkNode, EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode>> graph = GraphMLBuilder.Build<SocialNetworkNode>(graphML, HttpContext.Current.Server.MapPath("App_Data"));
            AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>> network = new AdjacencyGraph<IGraphMLComplexType, EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>>();
            foreach (EdgeWithAttributes.EdgeWithAttributes<SocialNetworkNode> edge in graph.Edges)
            {
                EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType> e = new EdgeWithAttributes.EdgeWithAttributes<IGraphMLComplexType>(edge.Source, edge.Target);
                network.AddVerticesAndEdge(e);
            }

            Type complex = typeof(ComplexNetCentralityLib.ComplexNetCentrality);
            complex.GetMethod(MethodName).Invoke(null, new object[] { network });

            return graph.GetInformationGraph();
        }

        [WebMethod]
        public Graph GetGraphFromGraphML(String graphML)
        {
            var network = GraphMLBuilder.Build<SocialNetworkNode>(graphML, HttpContext.Current.Server.MapPath("App_Data"));
            return network.GetInformationGraph();
        }

		public int CountInfectious(Simulation s) {
			int count = 0;
			var v = s.getSimulationResult();

			foreach (var item in v.Vertices) {
				if (item.state.stateNode.stateName == "Infectious") {
					count++;
				}
			}

			return count;
		}

		[WebMethod]
		public string[] GetAvailableMethodsOfGenerator() {
			MethodInfo[] methods = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethods();
			List<String> toReturn = new List<string>();

			foreach (var item in methods)
				if (item.Name.StartsWith("generate"))
					toReturn.Add(item.Name);

			return toReturn.ToArray();
		}

		[WebMethod]
		public string[] GetParametersFromMethod(string methodName) {
			MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
			List<string> toReturn = new List<string>();

			ParameterInfo[] parameters = method.GetParameters();
			foreach (var p in parameters)
				toReturn.Add(p.Name);

			return toReturn.ToArray();
		}

		[WebMethod]
		public string[] GetParametersTypesFromMethod(string methodName) {
			MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
			ParameterInfo[] parameters = method.GetParameters();
			string[] types = new string[parameters.Length];

			int i = 0;
			foreach (var item in parameters)
				types[i++] = item.ParameterType.ToString();

			return types;
		}

		[WebMethod]
		public Graph GenerateGraph(string methodName, object[] parameters) {
			MethodInfo method = typeof(ComplexNetGeneratorLib.ComplexNetGenerator).GetMethod(methodName);
			ComplexNetGenerator generator = new ComplexNetGenerator();

			ComplexNet net = method.Invoke(generator, parameters) as ComplexNet;

			Graph graph = CreateGraphFromComplexNet(net);

            //TODO:
//			DataBaseClassesDataContext db = new DataBaseClassesDataContext();
//			var nodes = from n in db.Nodes
//						select n;
//			var nodesArray = nodes.ToArray();
//
//			Random random = new Random();
//			foreach (var vertex in graph.Vertices) {
//				var n = nodesArray[random.Next() % nodesArray.Length];
//
//				vertex.Keys.Add(new PairKeyValue("birthdate", n.birthdate.ToString()));
//				vertex.Keys.Add(new PairKeyValue("country", n.country));
//				vertex.Keys.Add(new PairKeyValue("image", n.image.ToString()));//zmiana spowodowana innym sposobem przechowywania zdjec
//				vertex.Keys.Add(new PairKeyValue("latitude", n.latitude.ToString()));
//				vertex.Keys.Add(new PairKeyValue("longitude", n.longitude.ToString()));
//				vertex.Keys.Add(new PairKeyValue("name", n.name));
//				vertex.Keys.Add(new PairKeyValue("postalcode", n.postalcode));
//				vertex.Keys.Add(new PairKeyValue("street", n.street));
//				vertex.Keys.Add(new PairKeyValue("surname", n.surname));
//				vertex.Keys.Add(new PairKeyValue("town", n.town));
//			}

			return graph;
		}

		private Graph CreateGraphFromComplexNet(ComplexNet net) {
            Graph g = new Graph();
            g.ID = Guid.NewGuid();
            Dictionary<ComplexNetNode, GraphElement> map = new Dictionary<ComplexNetNode, GraphElement>();
            int i = 0;
            g.Vertices = new List<GraphElement>();
            foreach (ComplexNetNode node in net.nodes)
            {
                GraphElement v = new GraphElement()
                {
                    ID = Guid.NewGuid(),
                    GraphID = g.ID,
                    Keys = new List<PairKeyValue>()
                };
                v.Keys.Add(new PairKeyValue()
                {
                    ID = Guid.NewGuid(),
                    Key = v.ID + "_Key",
                    Value = v.ID + "_Value"
                });
                map.Add(node, v);
                g.Vertices.Add(v);
            }

            i = 0;
            g.Edges = new List<GraphEdge>();
            foreach (ComplexNetEdge edge in net.edges)
            {
                GraphEdge e = new GraphEdge()
                {
                    ID = Guid.NewGuid(),
                    GraphID = g.ID,
                    Source = map[edge.source].ID,
                    Target = map[edge.target].ID
                };
                g.Edges.Add(e);
            }

            return g;
		}

		[WebMethod]
		public string[] GetDiseaseModelStateNames() {
			List<String> states = new List<string>();

			Type t = typeof(State);

			Assembly asm = t.Assembly;

			foreach (Type type in asm.GetTypes()) {
				if (type.BaseType == t) {
					String val = type.FullName;
					states.Add(val);
				}
			}

			return states.ToArray();
		}

        [WebMethod(EnableSession = true)]
		public void changeNodeState(String token, int nodeId, String newStateName) {
            Simulation s = Context.Session["simulation"] as Simulation;
            if (s == null) return;

			s.changeNodeState(nodeId, newStateName);
		}

        [WebMethod(EnableSession = true)]
		public void removeNode(String token, int nodeId) {
            Simulation s = Context.Session["simulation"] as Simulation;
            if (s == null) return;

			s.removeNode(nodeId);
		}

        [WebMethod(EnableSession = true)]
		public void removeEdge(String token, int sourceId, int targetId) {
            Simulation s = Context.Session["simulation"] as Simulation;
            if (s == null) return;

			s.removeEdge(sourceId, targetId);
		}

		[WebMethod]
		public string[] GetAvailableStateChangeStrategies() {
			List<String> strategies = new List<string>();
			Type t = typeof(EpidemicSimulator.strategies.StateChangeStrategy);
			Assembly asm = t.Assembly;
			foreach (Type type in asm.GetTypes()) {
				Type[] interfaces = type.GetInterfaces();
				foreach (Type interFace in interfaces) {

					if (interFace.FullName == t.FullName) {
						String val = type.Name;
						strategies.Add(val);
					}
				}
			}
			return strategies.ToArray();
		}

		[WebMethod]
		public string[] GetAvailableStopConditions() {
			List<String> stopConditions = new List<string>();
			Type t = typeof(EpidemicSimulator.stopconditions.StopCondition);
			Assembly asm = t.Assembly;
			foreach (Type type in asm.GetTypes()) {
				Type[] interfaces = type.GetInterfaces();
				foreach (Type interFace in interfaces) {

					if (interFace.FullName == t.FullName) {
						String val = type.Name;
						stopConditions.Add(val);
					}
				}
			}
			return stopConditions.ToArray();
		}

        [WebMethod(EnableSession = true)]
		public SimulationReport getSimulationReport(String token) {
            Simulation s = Context.Session["simulation"] as Simulation;
            if (s == null) return null;

            return s.getSimulationReport();
		}

		[WebMethod]
		public double[] GetGeoCode(string strAddress) {
			string strKey = "ABQIAAAAt21w088otvkx4dydGkSFzhTiWYdC4kK5HJo75hRqCbZEyPurWxRFE2iDTU5WiKeD_zuNhg5zVaDZbw";

			string sPath = "http://maps.google.com/maps/geo?q=" + strAddress + "&output=csv&key=" + strKey;


			WebClient client = new WebClient();

			string[] eResult = client.DownloadString(sPath).ToString().Split(',');

			string latitude = eResult.GetValue(2).ToString();
			string longitude = eResult.GetValue(3).ToString();

			return new double[] { Convert.ToDouble(latitude, NumberFormatInfo.InvariantInfo), Convert.ToDouble(longitude, NumberFormatInfo.InvariantInfo) };
		}

        [WebMethod]
        public string netToGraphml(string netName)
        {
            DataBaseClassesDataContext db = new DataBaseClassesDataContext();
            if (db.DatabaseExists() == false) return null;
            var net = (from n in db.Nets where n.netName == netName select n).Single();

			string graphml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" "
				+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + "xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n";
			graphml += "\t<key id=\"id\" for=\"node\" attr.name=\"id\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"name\" for=\"node\" attr.name=\"name\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"surname\" for=\"node\" attr.name=\"surname\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"birthdate\" for=\"node\" attr.name=\"birthdate\" attr.type=\"string\" />\n";
            graphml += "\t<key id=\"photo\" for=\"node\" attr.name=\"photo\" attr.type=\"string\" />\n";
            graphml += "\t<key id=\"location\" for=\"node\" attr.name=\"location\" attr.type=\"string\" />\n";
            graphml += "\t<key id=\"locationX\" for=\"node\" attr.name=\"locationX\" attr.type=\"string\" />\n";
            graphml += "\t<key id=\"locationY\" for=\"node\" attr.name=\"locationY\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"street\" for=\"node\" attr.name=\"street\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"town\" for=\"node\" attr.name=\"town\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"postalcode\" for=\"node\" attr.name=\"postalcode\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"country\" for=\"node\" attr.name=\"country\" attr.type=\"string\" />\n";
			graphml += "\t<key id=\"stateName\" for=\"node\" attr.name=\"stateName\" attr.type=\"string\" />\n";
			graphml += "\t<graph edgedefault=\"undirected\">\n";

            var nodes = from n in db.Nodes select n;
            foreach(var n in nodes)
            {
				graphml += "\t\t<node id=\"node" + n.nodeID + "\">\n";
				graphml += "\t\t\t<data key=\"id\">n" + n.nodeID + "</data>\n";
				graphml += "\t\t\t<data key=\"name\">" + n.name + "</data>\n";
				graphml += "\t\t\t<data key=\"surname\">" + n.surname + "</data>\n";
				graphml += "\t\t\t<data key=\"birthdate\">" + n.birthdate + "</data>\n";
                graphml += "\t\t\t<data key=\"photo\">http://nosoifighters.studentlive.pl/GetImage.aspx?id=" + n.image + "</data>\n";
                graphml += "\t\t\t<data key=\"location\">" + n.country + "</data>\n";
                graphml += "\t\t\t<data key=\"locationX\">" + ((double)n.latitude).ToString(CultureInfo.CreateSpecificCulture("en-GB")) + "</data>\n";
                graphml += "\t\t\t<data key=\"locationY\">" + ((double)n.longitude).ToString(CultureInfo.CreateSpecificCulture("en-GB")) + "</data>\n";
				graphml += "\t\t\t<data key=\"street\">" + n.street + "</data>\n";
				graphml += "\t\t\t<data key=\"town\">" + n.town + "</data>\n";
				graphml += "\t\t\t<data key=\"postalcode\">" + n.postalcode + "</data>\n";
				graphml += "\t\t\t<data key=\"country\">" + n.country + "</data>\n";
				graphml += "\t\t\t<data key=\"stateName\">" + "Susceptible" + "</data>\n";
				graphml += "\t\t</node>\n";
            }

            var edges = (from e in db.Edges where e.netID == net.netID select e);
            foreach (var e in edges)
            {
				graphml += "\t\t<edge id=\"" + e.edgeID + "\" source=\"node" + e.leftNodeID + "\" target=\"node" + e.rightNodeID + "\" />\n";
            }

			graphml += "\t</graph>\n</graphml>\n";
            return graphml;
        }
	}
}
